$$ \newcommand{\floor}[1]{\left\lfloor{#1}\right\rfloor} \newcommand{\ceil}[1]{\left\lceil{#1}\right\rceil} \renewcommand{\mod}{\,\mathrm{mod}\,} \renewcommand{\div}{\,\mathrm{div}\,} \newcommand{\metar}{\,\mathrm{m}} \newcommand{\cm}{\,\mathrm{cm}} \newcommand{\dm}{\,\mathrm{dm}} \newcommand{\litar}{\,\mathrm{l}} \newcommand{\km}{\,\mathrm{km}} \newcommand{\s}{\,\mathrm{s}} \newcommand{\h}{\,\mathrm{h}} \newcommand{\minut}{\,\mathrm{min}} \newcommand{\kmh}{\,\mathrm{\frac{km}{h}}} \newcommand{\ms}{\,\mathrm{\frac{m}{s}}} \newcommand{\mss}{\,\mathrm{\frac{m}{s^2}}} \newcommand{\mmin}{\,\mathrm{\frac{m}{min}}} \newcommand{\smin}{\,\mathrm{\frac{s}{min}}} $$

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Приказивање готових слика

Све слике које смо до сада цртали, састављали смо од основних облика, као што су линије, правоугаоници, елипсе и многоуглови. Осим што се могу формирати на овакав начин, слике се могу учитати и из фајла као готове, а затим само приказати на екрану.

У многим ситуацијама нам је згодно да имамо неколико готових, унапред познатих и припремљених слика, које желимо да приказујемо на разним местима на екрану и није нам потребно да учитавамо произвољне слике. У таквом случају, најбоље је да одабране слике уградимо (енгл. embed) у сам пројекат. То значи да ће ове слике бити уграђене у извршиви фајл (.exe) и неће бити потребно да их копирамо као посебне фајлове.

У следећем примеру ћемо показати како се то ради.

Почетна позиција у шаху

Приказати помоћу програма почетну позицију у шаху.

Претпоставити при томе да су нам на располагању појединачне слике свих фигура, као и празне табле.

../_images/white_king.png ../_images/white_queen.png ../_images/white_rook.png ../_images/white_bishop.png ../_images/white_knight.png ../_images/white_pawn.png

../_images/black_king.png ../_images/black_queen.png ../_images/black_rook.png ../_images/black_bishop.png ../_images/black_knight.png ../_images/black_pawn.png

../_images/chess_table.png

Прво ћемо у прозору Solution Explorer раширити секцију Properties нашег пројекта и у њој пронаћи фајл Resources.resx.

../_images/resources.png

Двокликом на Resources.resx отварамо истоимени прозор. Овај прозор нам омогућава да у пројекат уградимо различите ресурсе. Пошто желимо да додамо слике, бирамо ставку Images из менија. На тај начин можемо да видимо које слике су уграђене у пројекат као ресурси.

../_images/resource_type1.png ../_images/resource_type2.png

Слике можемо да додајемо на разне начине. Пошто имамо готове слике, из менија Add Resource бирамо ставку Add Existing File… чиме отварамо дијалог за бирање фајла са сликом.

../_images/resource_type3.png

Када додамо све слике, прозор са ресурсима изгледа овако:

../_images/resources_added.png

Oве слике сада можемо једноставно да употребљавамо из програма. Довољно је да формирамо објекте типа Image, на пример овако

Image slTabla = Properties.Resources.SahTabla;

или низове таквих објеката, овако

Image[] slFigura = new Image[] {
    Properties.Resources.BeliKralj, Properties.Resources.BelaDama, Properties.Resources.BeliTop,
    Properties.Resources.BeliLovac, Properties.Resources.BeliSkakac, Properties.Resources.BeliPesak,
    Properties.Resources.CrniKralj, Properties.Resources.CrnaDama, Properties.Resources.CrniTop,
    Properties.Resources.CrniLovac, Properties.Resources.CrniSkakac, Properties.Resources.CrniPesak
};

Касније у програму, користимо неку од многобројних функција g.DrawImage (метода DrawImage класе Graphics) да бисмо приказали слику на екрану. Све ове функције имају параметре који на неки начин говоре функцији коју слику треба приказати и где је и како треба приказати. Овде ћемо употребити функцију која као параметре има један објекат типа Image и две целобројне координате које представљају позицију горњег левог угла слике:

g.DrawImage(slika, x, y);

На пример, таблу на којој се налази бели пешак на пољу d2 можемо да прикажемо овако:

g.DrawImage(slTabla, 0, 0);
g.DrawImage(slFigura[5], 150, 300);

Приметимо да је број 5 индекс слике белог пешака у низу slFigura.

../_images/chess_table2.png

Координате за целу таблу су (0, 0) јер таблу приказујемо тако да је њен горњи леви угао у горњем левом углу формулара.

При рачунању координата (150, 300) за белог пешака користимо чињеницу да је величина слике табле \(400 \times 400\), а величина слике пешака, као и слика осталих фигура и сваког поља табле \(50 \times 50\). Према томе, леве ивице колона табле имају редом x координате једнаке 0 (колона a), 50 (колона b), 100 (колона c), … а горње ивице редова имају y координате једнаке 0 (осми ред у шаховској нотацији, у којој се редови броје одоздо), 50 (седми ред), 100 (шести ред)…

Сада бисмо почетну позицију могли да прикажемо пишући по један позив функције g.DrawImage за сваку фигуру. Како у почетној позицији има 32 фигуре, то није најелегантнији начин да решимо задатак. Уместо тога, определили смо се за мало другачији приступ. Увели смо следеће константе као имена за индексе слика појединих фигура у низу slFigura

const int pp = -1, bK = 0, bD = 1, bT = 2, bL = 3, bS = 4, bP = 5;
const int cK = 6, cD = 7, cT = 8, cL = 9, cS = 10, cP = 11;

Овде pp значи празно поље, bK значи бели краљ, bD бела дама итд. Сада ове индексе користимо као вредности у матрици polje величине \(8 \times 8\), да бисмо описали позицију. Матрица polje нам за свако поље говори која фигура се на њему налази, па захваљујући томе можемо да прикажемо позицију помоћу двоструке петље.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    int[,] polje =
    {
        { bT, bS, bL, bD, bK, bL, bS, bT },
        { bP, bP, bP, bP, bP, bP, bP, bP },
        { pp, pp, pp, pp, pp, pp, pp, pp },
        { pp, pp, pp, pp, pp, pp, pp, pp },
        { pp, pp, pp, pp, pp, pp, pp, pp },
        { pp, pp, pp, pp, pp, pp, pp, pp },
        { cP, cP, cP, cP, cP, cP, cP, cP },
        { cT, cS, cL, cD, cK, cL, cS, cT }
    };
    g.DrawImage(slTabla, 0, 0);
    for (int red = 0; red < 8; red++)
    {
        for (int kol = 0; kol < 8; kol++)
        {
            int fig = polje[red, kol];
            if (fig >= 0)
                g.DrawImage(slFigura[fig], kol * 50, (7 - red) * 50);
        }
    }
}
../_images/chess_table3.png

Када се позиција приказује на овај начин, није тешко изменити програм да приказује било коју другу позицију, или чак да у неком (текстуалном) формату учитава позицију коју треба да прикаже.

Пример - воћњак

Дате су слике стабла и плода јабуке. Приказати воћњак налик следећој слици.

../_images/orchard.png

На исти начин као у претходном примеру, додајемо потребне слике међу ресурсе пројекта:

../_images/orchard_resources.png

На даље ћемо под јабуком подразумевати плод јабуке.

Видимо да јабуке на сваком дрвету имају исти распоред. То може да нам да идеју како да организујемо исцртавање. Формираћемо два низа тачака: један низ ће представљати позиције дрвећа, а други низ - позиције јабука на једном дрвету. При томе позиције јабука задајемо у односу на дрво, дакле позиција јабуке се задаје као разлика координата (горњих левих темена) слика јабуке и дрвета. Када формирамо поменуте низове, јабуке цртамо у двострукој петљи (на сваком дрвету сваку јабуку). Коначне координате јабуке у прозору добијамо као збир координата дрвета и јабуке у односу на дрво.

Point[] pozJabuke = new Point[] { ... }
Point[] pozDrveta = new Point[] { ... }
foreach (var mestoZaDrvo in pozDrveta)
{
    g.DrawImage(slDrvo, mestoZaDrvo);
    foreach (var mestoZaJabuku in pozJabuke)
        g.DrawImage(slJabuka, mestoZaDrvo.X + mestoZaJabuku.X, mestoZaDrvo.Y + mestoZaJabuku.Y);
}

При томе је важно да редослед цртања буде баш овакав: једно дрво, па јабуке на њему, па следеће дрво, па јабуке на њему итд. Размислите зашто слика не би изгледала добро ако бисмо цртали на пример прво сво дрвеће па све јабуке, или јабуке пре дрвета (можете и да испробате).

Код редоследа исцртавања важно је и којим редом су наведене позиције дрвећа у низу pozDrveta. На оваквим сликама оно што је нацртано ниже замишљамо као ближе. Због тога, када се слике два дрвета преклапају, треба прво нацртати оно са мањом y координатом, јер њега замишљамо као даље. Да бисмо ово осигурали, довољно је да позиције дрвећа наведемо тако да y координате тих позиција буду у растућем редоследу.

Код дрвећа које се међусобно не преклапа, редослед исцртавања није битан, а самим тим ни редослед навођења позиција дрвећа. То значи да наведени редослед за растућим y координатама у низу pozDrveta није једини добар (на пример, наш редослед је незнатно другачији, али и даље добар).

Природнијем изгледу слике можемо да допринесемо тако што ћемо положају сваке јабуке додати малу случајну вредност. На тај начин неће свако дрво изгледати потпуно исто. Генератор случајних вредности се користи врло једноставно, тако да ће вам вероватно све бити јасно из примера. Класа Random има три методе које се зову Next, а које враћају случајан цео број. Овде је поново од велике помоћи могућност окружења Visual Studio да аутоматски комплетира наредбе.

Ми смо коистили ону методу Next, којој се као параметри задају најмања и највећа дозвољена вредност (јабуке померамо до 15 пиксела горе-доле и до 15 пиксела лево-десно). Ево како може да изгледа цео програм:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Vocnjak
{
    public partial class Form1 : Form
    {
        Image slJabuka = Properties.Resources.jabuka;
        Image slDrvo = Properties.Resources.drvo;
        Random rnd = new Random();

        public Form1()
        {
            InitializeComponent();
            ClientSize = new Size(600, 400);
            Text = "Voćnjak";
            BackColor = Color.DarkGreen;
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Point[] pozJabuke = new Point[] {
                new Point(33, 131), new Point(51, 98),new Point(73, 108),
                new Point(114, 85), new Point(124, 115), new Point(150, 120) };
            Point[] pozDrveta = new Point[] {
                new Point(100, 0), new Point(20, 80), new Point(380, 25), new Point(300, 110), new Point(165, 155)};
            foreach (var mestoZaDrvo in pozDrveta)
            {
                g.DrawImage(slDrvo, mestoZaDrvo);
                foreach (var mestoZaJabuku in pozJabuke)
                    g.DrawImage(slJabuka,
                        mestoZaDrvo.X + mestoZaJabuku.X + rnd.Next(-15, 15),
                        mestoZaDrvo.Y + mestoZaJabuku.Y + rnd.Next(-15, 15));
            }
        }
    }
}
../_images/orchard_rnd.png

Надамо се да вам ове могућности дају разне идеје. Можете да припремите слике за свој пројекат у неком програму за цртање, а онда да их у свом C# програму комбинујете и приказујете на више места.

Ако одлучите да сами правите слике, након што их нацртате и сачувате, можда ће вам бити корисно да у неком од програма за обраду слике једну од боја (боју која представља позадину) прогласите за транспарентну (на нашим сликама из ове лекције боја позадине је транспарентна).